#
# This file is a part of the open-eBackup project.
# This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
# If a copy of the MPL was not distributed with this file, You can obtain one at
# http://mozilla.org/MPL/2.0/.
#
# Copyright (c) [2024] Huawei Technologies Co.,Ltd.
#
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
#

import os

from common.const import CopyDataTypeEnum
from common.logger import Logger
from db2.comm.const import Db2JsonConstant
from db2.comm.util.common_util import Db2CommonUtil
from db2.comm.util.dpf_util import DpfUtil
from db2.comm.util.param_util import Db2ParamUtil

LOGGER = Logger().get_logger(filename="db2.log")


class DpfTsRestoreService:
    @staticmethod
    def handle_full_restore_ts(os_user, db_name, res_ts_names, param_dict):
        """表空间集全量副本恢复"""
        cache_path = Db2ParamUtil.get_cache_mount_path_for_restore(param_dict)
        tmp_log_dir = DpfUtil.create_tmp_log_dir(cache_path, os_user)
        copies = Db2ParamUtil.parse_copies(param_dict)
        full_copy = Db2ParamUtil.get_copies_by_copy_type(copies, CopyDataTypeEnum.FULL_COPY.value)[0]
        full_copy_path = Db2ParamUtil.get_full_copy_path_when_restore(param_dict)
        full_bak_time = Db2ParamUtil.handle_backup_image_timestamp_of_copy(full_copy)
        ts_names_str = ",".join(res_ts_names)
        # 恢复表空间集
        full_restore_ts_cmd = f'su - {os_user} -c "db2_all \\"db2 restore db {db_name} tablespace \({ts_names_str}\) ' \
                              f'from {full_copy_path} taken at {full_bak_time} ' \
                              f'logtarget {tmp_log_dir} without prompting\\""'
        Db2CommonUtil.check_os_user_with_ex(os_user)
        Db2CommonUtil.check_injection(db_name, ts_names_str, str(full_bak_time))
        Db2CommonUtil.check_path_list(full_copy_path, tmp_log_dir)
        Db2CommonUtil.disconnect_db_application(os_user, db_name)
        Db2CommonUtil.deactivate_db_status(os_user, db_name)
        DpfUtil.execute_restore_ts(full_restore_ts_cmd)
        DpfUtil.check_rollforward_pending_after_exec_restore_ts(os_user, db_name)
        # 前滚表空间集
        rollforward_ts_cmd = f'su - {os_user} -c "db2 rollforward database {db_name} to end of backup and complete ' \
                             f'tablespace \({ts_names_str}\) online overflow log path \({tmp_log_dir}\)"'
        DpfUtil.execute_rollforward_ts(rollforward_ts_cmd)
        # 离线备份表空间集
        offline_bak_ts_cmd = f'su - {os_user} -c "db2 backup db {db_name} on all nodes tablespace \({ts_names_str}\) ' \
                             f'to /dev/null"'
        DpfUtil.execute_offline_backup_ts(offline_bak_ts_cmd)

    @staticmethod
    def handle_incr_restore_ts(os_user, db_name, res_ts_names, param_dict):
        """表空间集增量副本恢复"""
        cache_path = Db2ParamUtil.get_cache_mount_path_for_restore(param_dict)
        tmp_log_dir = DpfUtil.create_tmp_log_dir(cache_path, os_user)
        copies = Db2ParamUtil.parse_copies(param_dict)
        job_id = param_dict.get(Db2JsonConstant.JOB, {}).get(Db2JsonConstant.JOB_ID)
        full_copy_path = Db2ParamUtil.get_full_copy_path_for_incr_and_diff_restore(copies, job_id=job_id)
        incr_copy_paths = Db2ParamUtil.get_incr_copy_paths(copies, job_id=job_id)
        # 将增量副本文件软连接到全量副本目录
        for tmp_incr_copy_path in incr_copy_paths:
            for tmp_f_n in os.listdir(tmp_incr_copy_path):
                tmp_abs_path = os.path.realpath(os.path.join(tmp_incr_copy_path, tmp_f_n))
                if os.path.isfile(tmp_abs_path):
                    Db2CommonUtil.create_soft_link(tmp_abs_path, os.path.join(full_copy_path, tmp_f_n))
        full_copy = Db2ParamUtil.get_copies_by_copy_type(copies, CopyDataTypeEnum.FULL_COPY.value)[0]
        all_incr_copies = Db2ParamUtil.get_copies_by_copy_type(copies, CopyDataTypeEnum.INCREMENT_COPY.value)
        # 手动恢复顺序：最后一个增量->全量->依次从第一个增量到最后一个增量
        manual_restore_copies = [all_incr_copies[-1], full_copy] + all_incr_copies
        ts_names_str = ",".join(res_ts_names)
        Db2CommonUtil.disconnect_db_application(os_user, db_name)
        Db2CommonUtil.deactivate_db_status(os_user, db_name)
        Db2CommonUtil.check_os_user_with_ex(os_user)
        Db2CommonUtil.check_injection(db_name, ts_names_str)
        Db2CommonUtil.check_path_list(full_copy_path, tmp_log_dir)
        restore_num = len(manual_restore_copies)
        for idx, tmp_restore_copy in enumerate(manual_restore_copies):
            # 只在最后一次restore时提取日志
            log_target_cmd = f"logtarget {tmp_log_dir} " if idx == (restore_num - 1) else ""
            tmp_copy_bak_time = Db2ParamUtil.handle_backup_image_timestamp_of_copy(tmp_restore_copy)
            Db2CommonUtil.check_injection(str(tmp_copy_bak_time))
            tmp_restore_ts_cmd = f'su - {os_user} -c "db2_all \\"db2 restore db {db_name} tablespace ' \
                                 f'\({ts_names_str}\) incremental from {full_copy_path} ' \
                                 f'taken at {tmp_copy_bak_time} {log_target_cmd}without prompting\\""'
            DpfUtil.execute_restore_ts(tmp_restore_ts_cmd)
        DpfUtil.check_rollforward_pending_after_exec_restore_ts(os_user, db_name)
        rollforward_ts_cmd = f'su - {os_user} -c "db2 rollforward database {db_name} to end of backup and complete ' \
                             f'tablespace \({ts_names_str}\) online overflow log path \({tmp_log_dir}\)"'
        DpfUtil.execute_rollforward_ts(rollforward_ts_cmd)
        offline_bak_ts_cmd = f'su - {os_user} -c "db2 backup db {db_name} on all nodes tablespace \({ts_names_str}\) ' \
                             f'to /dev/null"'
        DpfUtil.execute_offline_backup_ts(offline_bak_ts_cmd)

    @staticmethod
    def handle_diff_restore_ts(os_user, db_name, res_ts_names, param_dict):
        """表空间集差异副本恢复"""
        cache_path = Db2ParamUtil.get_cache_mount_path_for_restore(param_dict)
        tmp_log_dir = DpfUtil.create_tmp_log_dir(cache_path, os_user)
        copies = Db2ParamUtil.parse_copies(param_dict)
        job_id = param_dict.get(Db2JsonConstant.JOB, {}).get(Db2JsonConstant.JOB_ID)
        full_copy_path = Db2ParamUtil.get_full_copy_path_for_incr_and_diff_restore(copies, job_id=job_id)
        diff_copy_path = Db2ParamUtil.get_diff_copy_path(copies, job_id=job_id)
        # 将差异副本文件软连接到全量副本目录
        for tmp_f_n in os.listdir(diff_copy_path):
            tmp_abs_path = os.path.realpath(os.path.join(diff_copy_path, tmp_f_n))
            if os.path.isfile(tmp_abs_path):
                Db2CommonUtil.create_soft_link(tmp_abs_path, os.path.join(full_copy_path, tmp_f_n))
        full_copy = Db2ParamUtil.get_copies_by_copy_type(copies, CopyDataTypeEnum.FULL_COPY.value)[0]
        diff_copy = Db2ParamUtil.get_copies_by_copy_type(copies, CopyDataTypeEnum.DIFF_COPY.value)[0]
        # 手动恢复顺序：差异->全量->差异
        manual_restore_copies = [diff_copy, full_copy, diff_copy]
        ts_names_str = ",".join(res_ts_names)
        Db2CommonUtil.disconnect_db_application(os_user, db_name)
        Db2CommonUtil.deactivate_db_status(os_user, db_name)
        Db2CommonUtil.check_os_user_with_ex(os_user)
        Db2CommonUtil.check_injection(db_name, ts_names_str)
        Db2CommonUtil.check_path_list(full_copy_path, tmp_log_dir)
        restore_num = len(manual_restore_copies)
        for idx, tmp_restore_copy in enumerate(manual_restore_copies):
            # 只在最后一次restore时提取日志
            log_target_cmd = f"logtarget {tmp_log_dir} " if idx == (restore_num - 1) else ""
            tmp_copy_bak_time = Db2ParamUtil.handle_backup_image_timestamp_of_copy(tmp_restore_copy)
            Db2CommonUtil.check_injection(str(tmp_copy_bak_time))
            tmp_restore_ts_cmd = f'su - {os_user} -c "db2_all \\"db2 restore db {db_name} tablespace ' \
                                 f'\({ts_names_str}\) incremental from {full_copy_path} ' \
                                 f'taken at {tmp_copy_bak_time} {log_target_cmd}without prompting\\""'
            DpfUtil.execute_restore_ts(tmp_restore_ts_cmd)
        DpfUtil.check_rollforward_pending_after_exec_restore_ts(os_user, db_name)
        rollforward_ts_cmd = f'su - {os_user} -c "db2 rollforward database {db_name} to end of backup and complete ' \
                             f'tablespace \({ts_names_str}\) online overflow log path \({tmp_log_dir}\)"'
        DpfUtil.execute_rollforward_ts(rollforward_ts_cmd)
        offline_bak_ts_cmd = f'su - {os_user} -c "db2 backup db {db_name} on all nodes tablespace \({ts_names_str}\) ' \
                             f'to /dev/null"'
        DpfUtil.execute_offline_backup_ts(offline_bak_ts_cmd)
